<?php


namespace GordenSong\Utils;

/**
 * Class RuleFormatter
 * @package GordenSong\Utils
 */
class RuleUtil
{
	/**
	 * [min:1,string ] => [min=>min:1, string=>string]
	 *
	 * @param $rules
	 * @return array
	 */
	public static function getKeyRules($rules): array
	{
		$rules = self::flatten($rules);

		return self::keyByRules($rules);
	}

	/**
	 * @param array|string[] $rules
	 * @return array
	 */
	public static function normalizeRule(array $rules): array
	{
		$keyRules = self::getKeyRules($rules);

		foreach (['required', 'bail'] as $item) {
			if (array_key_exists($item, $keyRules)) {
				unset($keyRules[$item]);
				array_unshift($keyRules, $item);
			}
		}

		return array_values($keyRules);
	}

	public static function normalizeManyRules(array $rules): array
	{
		foreach ($rules as $key => $rule) {
			$rules[$key] = self::normalizeRule($rule);
		}
		return $rules;
	}

	/**
	 * 转成一维数组
	 *
	 * ['a|b'] => ['a', 'b']
	 *
	 * @param array|string[]|string $rules
	 * @return array|string[]
	 */
	public static function flatten($rules): array
	{
		$rules = (array)$rules;

		$result = [];
		foreach ($rules as $rule) {
			if (is_string($rule)) {
				$result = array_merge($result, (array)$rule);
			} elseif (is_array($rule)) {
				$result = array_merge($result, self::flatten($rule));
			} elseif (is_callable($rule) || is_object($rule)) {
				$result[] = $rule;
			}
		}

		return $result;
	}

	/**
	 * ['min:1'] -> ['min'=>'min:1']
	 * @param array $rules
	 * @return array
	 */
	public static function keyByRules(array $rules): array
	{
		$result = [];
		foreach ($rules as $key => $piece) {
			if (is_string($piece)) {
				[$key,] = explode(':', $piece, 2);
				$result[$key] = $piece;
			} elseif (is_callable($piece) || is_object($piece)) {
				$result[] = $piece;
			} else {
				throw new \InvalidArgumentException();
			}
		}
		return $result;
	}

	/**
	 * eg: merge one field rules with another field rules
	 *
	 * @param array $rules ['required']
	 * @param array $rules2 ['string']
	 * @return array
	 */
	public static function mergeRules(array $rules, array $rules2): array
	{
		return array_merge(self::getKeyRules($rules), self::getKeyRules($rules2));
	}

	/**
	 * [a => [string, required]] - [a => [string]] => [a => [required]]
	 * @param array $rules1
	 * @param array $rules2
	 * @return array
	 */
	public static function diffRules(array $rules1, array $rules2): array
	{
		$rules1 = self::getKeyRules($rules1);
		$rules2 = self::getKeyRules($rules2);

		foreach ($rules2 as $key => $value) {
			unset($rules1[$key]);
		}
		return array_values($rules1);
	}


	/**
	 * @param array $rules ['a' => ['string'], 'b' =>['string']]
	 * @param array $rules2 ['a' => ['required'], 'b' =>['required']]
	 * @return array
	 */
	public static function mergeManyRules(array $rules, array $rules2): array
	{
		$ruleList = array_merge_recursive($rules, $rules2);
		foreach ($ruleList as $key => $rules) {
			$ruleList[$key] = RuleUtil::normalizeRule($rules);
		}

		return $ruleList;
	}

	/**
	 * <pre>
	 * $rules = ['title' => ['string', 'required'], 'created_at' => ['datetime']];
	 * $rules2 = ['title' => ['string'], 'created_at'];
	 * $diffRules = RuleUtil::diffManyRules($rules, $rules2);
	 * Assert::assertSame(['title' => ['required']], $diffRules);
	 * </pre>
	 * @param array $rules1
	 * @param array $rules2
	 * @return array
	 */
	public static function diffManyRules(array $rules1, array $rules2): array
	{
		$rules = [];
		foreach ($rules1 as $key => $values) {
			$rules[$key] = self::diffRules($values, data_get($rules2, $key, []));
		}
		foreach ($rules2 as $key => $value) {
			if (is_numeric($key)) {
			    unset($rules[$value]);
			}
		}
		return $rules;
	}
}
